package com.labofjet.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.core.Converter;

import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.PropertyValue;

import com.labofjet.bean.impl.CustomConverter;

/**
 * 1.List Map Object 对象的拷贝 2.数据库结果映射到对象 注意: BeanCopier 不能深拷贝集合属性
 * 
 * @author jyzjyz12@163.com
 * 
 */
public class ConverterUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(ConverterUtils.class);
    private static final Map<String, BeanCopier> CACHED_COPIER_MAP = new ConcurrentHashMap<String, BeanCopier>();
    private static final Map<String, CustomConverter> CACHED_CUSTOM_CONVERTER_MAP = new ConcurrentHashMap<String, CustomConverter>();

    /**
     * 拷贝一个对象
     * 
     * @param sourceObj
     * @param targetObj
     * @param converter
     * @param customerConverterClass
     * @return
     */
    public static <S, T> T convertOne(S sourceObj, T targetObj, Class<CustomConverter> customerConverterClass,
            Converter converter) {
        if (sourceObj == null || targetObj == null) {
            return null;
        }
        copy(sourceObj, targetObj, converter, customerConverterClass);
        return targetObj;
    }

    /**
     * 拷贝一个对象
     * 
     * @param sourceObj
     * @param targetObj
     * @return
     */
    public static <S, T> T convertOne(S sourceObj, T targetObj) {
        return convertOne(sourceObj, targetObj, null, null);
    }
    
    /**
     * 拷贝一个对象
     * 
     * @param sourceObj
     * @param targetObj
     * @return
     */
    public static <S, T> T convertOne(S sourceObj, T targetObj, CustomConverter converter) {
        if (sourceObj == null || targetObj == null) {
            return null;
        }
        copy(sourceObj, targetObj, null, converter);
        return targetObj;
    }

    /**
     * 拷贝集合
     * 
     * @param sourceObjList
     * @param targetClass
     * @param converter
     * @param converterClass
     * @return
     */
    public static <S, T> List<T> convertList(List<S> sourceObjList, Class<T> targetClass, Converter converter,
            Class<CustomConverter> converterClass) {
        if (sourceObjList == null) {
            return null;
        }
        List<T> targetObjList = new ArrayList<T>();
        for (S sourceObj : sourceObjList) {
            try {
                T TargetObj = targetClass.newInstance();
                copy(sourceObj, TargetObj, converter, converterClass);
                targetObjList.add(TargetObj);
            } catch (Exception e) {
                LOGGER.error("创建target对象失败", e);
            }
        }
        return targetObjList;
    }

    /**
     * 拷贝集合
     * 
     * @param sourceObjList
     * @param targetClass
     * @return
     */
    public static <S, T> List<T> convertList(List<S> sourceObjList, Class<T> targetClass) {
        return convertList(sourceObjList, targetClass, null, null);
    }

    /**
     * 拷贝Map,key不变,value拷贝
     * 
     * @param sourceObjMap
     * @param targetClass
     * @param converter
     * @param converterClass
     * @return
     */
    public static <K, S, T> Map<K, T> convertMap(Map<K, S> sourceObjMap, Class<T> targetClass, Converter converter,
            Class<CustomConverter> converterClass) {
        if (sourceObjMap == null) {
            return null;
        }
        Map<K, T> targetObjMap = new HashMap<K, T>();
        for (K key : sourceObjMap.keySet()) {
            S sourceObj = sourceObjMap.get(key);
            try {
                T TargetObj = targetClass.newInstance();
                copy(sourceObj, TargetObj, converter, converterClass);
                targetObjMap.put(key, TargetObj);
            } catch (Exception e) {
                LOGGER.error("创建target对象失败" + e);
            }
        }
        return targetObjMap;
    }

    /**
     * 拷贝Map,key不变,value拷贝
     * 
     * @param sourceObjMap
     * @param targetClass
     * @return
     */
    public static <K, S, T> Map<K, T> convertMap(Map<K, S> sourceObjMap, Class<T> targetClass) {
        return convertMap(sourceObjMap, targetClass, null, null);
    }

    /**
     * 将List中的属性设置到对象中去,一般用于数据库查询List的结果转化到对象中
     * 
     * @param list
     *            包含属性值的List
     * @param fieldNames
     *            要设置到的对象的属性名字
     * @param targetClass
     *            要设置到的对象的class
     * @return
     */
    public static <T> T convertListToObject(List<?> list, List<String> fieldNames, Class<T> targetClass) {
        T t = null;
        try {
            t = targetClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("不能创建对象", e);
        }
        if (CollectionUtils.isEmpty(list) || CollectionUtils.isEmpty(fieldNames)) {
            return t;
        }
        BeanWrapper wrapper = new BeanWrapperImpl(t);
        for (int i = 0; i < list.size(); i++) {
            wrapper.setPropertyValue(new PropertyValue(fieldNames.get(i), list.get(i)));
        }
        return t;
    }

    /**
     * 将List中的属性设置到对象中去,一般用于数据库查询List的结果转化到对象中
     * 
     * @param list
     *            包含属性值的List
     * @param fieldNames
     *            要设置到的对象的属性名字
     * @param targetClass
     *            要设置到的对象的class
     * @return
     */
    public static Object convertListToObject(List<?> list, List<String> fieldNames, Object targetObj) {
        if (CollectionUtils.isEmpty(list) || CollectionUtils.isEmpty(fieldNames)) {
            return targetObj;
        }
        BeanWrapper wrapper = new BeanWrapperImpl(targetObj);
        for (int i = 0; i < list.size(); i++) {
            wrapper.setPropertyValue(new PropertyValue(fieldNames.get(i), list.get(i)));
        }
        return targetObj;
    }

    /**
     * 将数据的查询结果的二维表转化成对象的List
     * 
     * @param rows
     * @param fieldNames
     * @param targetClass
     * @return
     */
    public static <T> List<T> convertDbResultToObjectList(List<Object[]> rows, List<String> fieldNames,
            Class<T> targetClass) {
        List<T> result = new ArrayList<T>();
        for (Object[] row : rows) {
            T t = convertListToObject(Arrays.asList(row), fieldNames, targetClass);
            result.add(t);
        }
        return result;
    }

    /**
     * 取得BeanCopier,有缓存先找缓存
     * 
     * @param sourceClass
     * @param targetClass
     * @param converter
     * @return
     */
    private static <S, T> BeanCopier getBeanCopier(Class<S> sourceClass, Class<T> targetClass, Converter converter) {
        String key = sourceClass.getName() + targetClass.getName();
        BeanCopier copier = CACHED_COPIER_MAP.get(key);
        if (copier == null) {
            copier = BeanCopier.create(sourceClass, targetClass, converter != null);
            CACHED_COPIER_MAP.put(key, copier);
        }
        return copier;
    }

    /**
     * 取得自定义converter,有缓存先找缓存
     * 
     * @param converterClass
     * @return
     * @author jiangyz
     */
    private static CustomConverter getCustomerConverter(Class<CustomConverter> converterClass) {
        if (converterClass == null) {
            return null;
        }
        String key = converterClass.getName();
        CustomConverter converter = CACHED_CUSTOM_CONVERTER_MAP.get(key);
        if (converter == null) {
            try {
                converter = converterClass.newInstance();
            } catch (Exception e) {
                LOGGER.error("创建converter失败" + e);
            }
            CACHED_CUSTOM_CONVERTER_MAP.put(key, converter);
        }
        return converter;
    }

    /**
     * 从source复制到target
     * 
     * @param source
     * @param target
     * @param converter
     * @param customerConverterClass
     */
    private static <S, T> void copy(S source, T target, Converter converter,
            Class<CustomConverter> customerConverterClass) {
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass(), converter);
        copier.copy(source, target, converter);
        CustomConverter customerConverter = getCustomerConverter(customerConverterClass);
        if (customerConverter != null) {
            customerConverter.convert(source, target);
        }
    }
    
    private static <S, T> void copy(S source, T target, Converter converter,
            CustomConverter customerConverter) {
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass(), converter);
        copier.copy(source, target, converter);
        if (customerConverter != null) {
            customerConverter.convert(source, target);
        }
    }
}
